package com.hollysys.hollimsa.biz.istio.virtualservice.service.impl;


import com.hollysys.hollimsa.biz.istio.virtualservice.dao.VirtualServiceDao;
import com.hollysys.hollimsa.biz.istio.virtualservice.domain.VirtualserviceDTO;
import com.hollysys.hollimsa.biz.istio.virtualservice.domain.VirtualserviceDetailDTO;
import com.hollysys.hollimsa.biz.istio.virtualservice.domain.VirtualserviceVO;
import com.hollysys.hollimsa.biz.istio.virtualservice.service.VirtualserviceService;
import com.hollysys.hollimsa.common.exception.BusinessApiException;
import com.hollysys.hollimsa.common.filter.tenantinfo.TenantInfoContext;
import com.hollysys.hollimsa.common.result.AjaxCodeEnum;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import lombok.extern.slf4j.Slf4j;
import me.snowdrop.istio.api.networking.v1beta1.*;
import me.snowdrop.istio.client.internal.operation.networking.v1beta1.VirtualServiceOperationImpl;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 路由规则
 *
 * @author wangyuxi
 * @date 2020/10/14
 **/
@Service
@Slf4j
public class VirtualserviceServiceImpl implements VirtualserviceService {

    private  static final String REQUEST_TYPE_PUBLIC =  "public";

    private  static final String REQUEST_TYPE_ANNO =  "requestType";

    private  static final String ROUTER_TYPE_ANNO =  "routerType";


    @Autowired
    private VirtualServiceDao virtualServiceDao;

    /**
     * 创建 虚拟服务
     *
     * @param vsvo 虚拟服务json对象
     * @return 创建或更新状态
     */
    @Override
    public boolean createOrReplaceVirtualservice(VirtualserviceVO vsvo) {
        final String namespace = TenantInfoContext.getTenantNS();
        virtualServiceDao.createOrReplaceVirtualservice(getVirtualServiceBuilder(vsvo, namespace), namespace);
        return Boolean.TRUE;
    }

    /**
     * 构建vs
     * @param vsvo
     * @param namespace
     * @return
     */
    private VirtualService getVirtualServiceBuilder(VirtualserviceVO vsvo, String namespace) {
        VirtualServiceBuilder vsb = new VirtualServiceBuilder();
        vsb.withSpec(getVirtualServiceSpecBuilder(vsvo, namespace, vsb));
        log.debug("New VirtualService created: {}", vsb.build());
        return vsb.build();
    }

    /**
     * 构建vs描述
     * @param vsvo
     * @param namespace
     * @param vsb
     * @return
     */
    private VirtualServiceSpec getVirtualServiceSpecBuilder(VirtualserviceVO vsvo, String namespace, VirtualServiceBuilder vsb) {



        VirtualServiceSpecBuilder vss = new VirtualServiceSpecBuilder();
        if (null == vsvo.getHosts() && vsvo.getHosts().length <= 0) {
            throw new BusinessApiException(AjaxCodeEnum.FAIL.getCode(),"Virtualservice's hosts not null or not empty!!");
        }
        /*服务域名*/
        vss.addToHosts(vsvo.getHosts());
        /*公网添加gateway*/
        if (REQUEST_TYPE_PUBLIC.equals(vsvo.getRequestType())) {
            vss.addToGateways(vsvo.getGateways());
        }

        vss.addToExportTo(".");

        Collection<HTTPRoute> httpRoutes = getHttpRoutes(vsvo);
        if (null != httpRoutes && !httpRoutes.isEmpty()) {
            vss.addAllToHttp(httpRoutes);
        }

        vsb.withMetadata(getObjectMetaBuilder(vsvo, namespace));
        return vss.build();
    }

    /**
     * 构建元数据信息
     *
     * @param vsvo
     * @param namespace
     * @return
     */
    private ObjectMeta getObjectMetaBuilder(VirtualserviceVO vsvo, String namespace) {
        /*规则名称*/
        ObjectMetaBuilder omb = new ObjectMetaBuilder();
        omb.withNamespace(namespace);
        omb.addToAnnotations(REQUEST_TYPE_ANNO, vsvo.getRequestType()).addToAnnotations(ROUTER_TYPE_ANNO, vsvo.getRouterType());
        omb.withName(vsvo.getRuleName());
        return omb.build();
    }

    /**
     * 构建路由项
     * @param vsvo
     * @return
     */
    private Collection<HTTPRoute> getHttpRoutes(VirtualserviceVO vsvo) {
        return Arrays.stream(vsvo.getHttp()).map(httpRoute -> {
            final HTTPRouteBuilder hrb = new HTTPRouteBuilder();

            if (Objects.nonNull(httpRoute.getMatch()) && httpRoute.getMatch().length > 0) {
                Arrays.stream(httpRoute.getMatch()).forEach(httpMatchRequest -> hrb.addToMatch(getHttpMatchRequestBuilder(httpMatchRequest)));
            }
            if (Objects.nonNull(httpRoute.getRoute()) && httpRoute.getRoute().length > 0) {
                Arrays.stream(httpRoute.getRoute()).forEach(httpRouteDestination -> hrb.addToRoute(getHttpRouteDestinationBuilder(httpRouteDestination)));
            }
            return hrb.build();
        }).collect(Collectors.toList());
    }


    private HTTPRouteDestination getHttpRouteDestinationBuilder(VirtualserviceVO.HTTPRouteDestination httpRouteDestination) {
        final VirtualserviceVO.Destination destination = httpRouteDestination.getDestination();
        final HTTPRouteDestinationBuilder rdb = new HTTPRouteDestinationBuilder();
        final DestinationBuilder db = new DestinationBuilder();
        db.withHost(destination.getHost());

        if (StringUtils.isNotBlank(destination.getSubset())) {
            db.withSubset(destination.getSubset());
        }
        if (null != destination.getPort() && null != destination.getPort().getNumber()) {
            db.withNewPort(destination.getPort().getNumber());
        }
        rdb.withDestination(db.build());

        if (null != httpRouteDestination.getWeight()) {
            rdb.withWeight(httpRouteDestination.getWeight());
        }
        return rdb.build();
    }


    private HTTPMatchRequest getHttpMatchRequestBuilder(VirtualserviceVO.HTTPMatchRequest httpMatchRequest) {
        String routeCondition = httpMatchRequest.getRouteCondition();
        String matchType = httpMatchRequest.getMatchType();
        String inputRule = httpMatchRequest.getInputRule();
        HTTPMatchRequestBuilder hmrb = new HTTPMatchRequestBuilder();
        //当前uri 且 prefix
        if ("uri".equals(routeCondition) && "prefix".equals(matchType)) {
            StringMatchBuilder smb = new StringMatchBuilder();
            PrefixMatchTypeBuilder pmtb = new PrefixMatchTypeBuilder();
            pmtb.withPrefix(inputRule);
            smb.withMatchType(pmtb.build());
            hmrb.withUri(smb.build());
        }
        if ("uri".equals(routeCondition) && "exact".equals(matchType)) {
            StringMatchBuilder smb = new StringMatchBuilder();
            ExactMatchTypeBuilder emtb = new ExactMatchTypeBuilder();
            emtb.withExact(inputRule);
            smb.withMatchType(emtb.build());
            hmrb.withUri(smb.build());
        }
        if ("uri".equals(routeCondition) && "regex".equals(matchType)) {
            StringMatchBuilder smb = new StringMatchBuilder();
            RegexMatchTypeBuilder rmtb = new RegexMatchTypeBuilder();
            rmtb.withRegex(inputRule);
            smb.withMatchType(rmtb.build());
            hmrb.withUri(smb.build());
        }
        return hmrb.build();
    }

    /**
     * @return 规则列表
     */
    @Override
    public List<VirtualserviceDTO> queryVirtualService() {
        final String namespace = TenantInfoContext.getTenantNS();
        return virtualServiceDao.queryVirtualService(namespace).list().getItems().stream()
            .map(this::getVirtualServiceDTO).collect(Collectors.toList());
    }

    private VirtualserviceDTO getVirtualServiceDTO(VirtualService vs) {

        Map<String, String> getAnnotations = vs.getMetadata().getAnnotations();

        /*路由规则名称*/
        String ruleName = vs.getMetadata().getName();

        /*创建时间*/
        Date createDate = DateTime.parse(vs.getMetadata().getCreationTimestamp()).toDate();

        VirtualserviceDTO dto = VirtualserviceDTO.builder()
            .ruleName(ruleName)
            .createdAt(createDate).build();

        if (null != getAnnotations) {
            /*默认集群内*/
            String requestType = getAnnotations.getOrDefault(REQUEST_TYPE_ANNO, "cluster");
            /*默认基于内容分发*/
            String routerType =  getAnnotations.getOrDefault(ROUTER_TYPE_ANNO, "content");

            dto.setRequestType(requestType);
            dto.setRouterType(routerType);

            /*有网关*/
            if (REQUEST_TYPE_PUBLIC.equals(requestType)) {
                /*网关名称*/
                dto.setGateways(vs.getSpec().getGateways());
                dto.setRequestType(REQUEST_TYPE_PUBLIC);
            }
        }
        return dto;
    }


    /**
     * 删除虚拟服务-路由规则
     *
     * @param name 虚拟服务名称
     * @return boolean 删除状态
     */
    @Override
    public boolean deleteVirtualService(String name) {
        final String namespace = TenantInfoContext.getTenantNS();
        return virtualServiceDao.deleteVirtualService(name,namespace);
    }


    /**
     * 查询虚拟服务详情
     *
     * @param name 路由规则名称
     * @return 路由详情表单信息
     */
    @Override
    public VirtualserviceDetailDTO queryVirtualDetail(String name) {
        final String namespace = TenantInfoContext.getTenantNS();
        VirtualService virtualService = virtualServiceDao.queryVirtualService(namespace).withName(name).get();
        if (null != virtualService) {
            return getVirtualServiceDetail(virtualService);
        }
        return null;
    }

    /**
     * 根据名称查询路由规则
     * @param name 路由规则名称
     * @return
     */
    @Override
    public VirtualserviceDTO queryVirtualServiceByName(String name) {
        final String namespace = TenantInfoContext.getTenantNS();

        VirtualServiceOperationImpl vsoi = virtualServiceDao.queryVirtualService(namespace);

        if (StringUtils.isNotBlank(name)) {
            VirtualService vs = vsoi.withName(name).get();
            if (Objects.nonNull(vs)) {
                return getVirtualServiceDTO(vs);
            }
        }
        return null;
    }

    /**
     * 获取虚拟服务详情
     *
     * @param vs 服务器中的 vs crd 文件
     * @return 路由详情表单信息
     */
    private VirtualserviceDetailDTO getVirtualServiceDetail(VirtualService vs) {

        final Map<String, String> getAnnotations = vs.getMetadata().getAnnotations();
        /*路由规则名称*/
        final String ruleName = vs.getMetadata().getName();

        final String[] hosts = vs.getSpec().getHosts().toArray(new String[0]);

        /*获取匹配项*/
        VirtualserviceDetailDTO.HttpRoute[] httpRoutes = getHttpRoutes(vs);

        VirtualserviceDetailDTO dto = VirtualserviceDetailDTO.builder()
            .ruleName(ruleName)
            .hosts(hosts)
            .http(httpRoutes).build();

        if (null != getAnnotations) {
            /*默认集群内*/
            String requestType = getAnnotations.getOrDefault(REQUEST_TYPE_ANNO, "cluster");
            /*默认基于内容分发*/
            String routerType = getAnnotations.getOrDefault(ROUTER_TYPE_ANNO, "content");

            dto.setRequestType(requestType);
            dto.setRouterType(routerType);

            /*有网关*/
            if (REQUEST_TYPE_PUBLIC.equals(requestType)) {
                /*网关名称*/
                dto.setGateways(vs.getSpec().getGateways().toArray(new String[0]));
                dto.setRequestType(REQUEST_TYPE_PUBLIC);
            }

        }
        return dto;
    }

    /***
     *  获取httpRoute
     *
     * @param vs 路由规则crd 描述
     * @return HttpRoute
     */
    private VirtualserviceDetailDTO.HttpRoute[] getHttpRoutes(VirtualService vs) {
        return vs.getSpec().getHttp().stream().map(httpRoute -> {

            VirtualserviceDetailDTO.HttpRoute myHttpRoute = new VirtualserviceDetailDTO.HttpRoute();

            /*绑定的匹配信息*/
            VirtualserviceDetailDTO.HTTPMatchRequest[] match = getHttpMatchRequests(httpRoute);
            /*绑定的组件信息*/
            VirtualserviceDetailDTO.HTTPRouteDestination[] route = getHttpRouteDestinations(httpRoute);

            //存在匹配项
            if (Arrays.stream(match).count() > 0) {
                myHttpRoute.setMatch(match);
            }
            myHttpRoute.setRoute(route);
            return myHttpRoute;
        }).toArray(VirtualserviceDetailDTO.HttpRoute[]::new);
    }


    /**
     * 获取绑定的组件信息
     * @param httpRoute
     * @return
     */
    private VirtualserviceDetailDTO.HTTPRouteDestination[] getHttpRouteDestinations(HTTPRoute httpRoute) {
        return httpRoute.getRoute().stream().map(httpRouteDestination -> {
            VirtualserviceDetailDTO.HTTPRouteDestination myHttpRouteDestination = new VirtualserviceDetailDTO.HTTPRouteDestination();

            VirtualserviceDetailDTO.Destination myDestination = new VirtualserviceDetailDTO.Destination();

            Destination destination = httpRouteDestination.getDestination();

            if (null != destination && StringUtils.isNotBlank(destination.getHost())) {
                myDestination.setHost(destination.getHost());
            }


            if (null != destination && null != destination.getPort() && null != destination.getPort().getNumber()) {
                VirtualserviceDetailDTO.PortSelector myPortSelector = new VirtualserviceDetailDTO.PortSelector();
                myPortSelector.setNumber(destination.getPort().getNumber());
                myDestination.setPort(myPortSelector);
            }

            if (null != destination && StringUtils.isNotBlank(destination.getSubset())) {
                myDestination.setSubset(destination.getSubset());
            }
            myHttpRouteDestination.setDestination(myDestination);

            if (null != httpRouteDestination.getWeight()) {
                myHttpRouteDestination.setWeight(httpRouteDestination.getWeight());
            }

            return myHttpRouteDestination;
        }).toArray(VirtualserviceDetailDTO.HTTPRouteDestination[]::new);
    }


    /**
     * 获取绑定的匹配信息
     * @param httpRoute
     * @return
     */
    private VirtualserviceDetailDTO.HTTPMatchRequest[] getHttpMatchRequests(HTTPRoute httpRoute) {
        return httpRoute.getMatch().stream().map(httpMatchRequest -> {

            VirtualserviceDetailDTO.HTTPMatchRequest myStringMatch = new VirtualserviceDetailDTO.HTTPMatchRequest();

            if (httpMatchRequest.getUri().getMatchType() instanceof PrefixMatchType) {
                PrefixMatchType prefixMatchType = (PrefixMatchType) httpMatchRequest.getUri().getMatchType();
                myStringMatch.setRouteCondition("uri");
                myStringMatch.setMatchType("prefix");
                myStringMatch.setInputRule(prefixMatchType.getPrefix());
            }

            if (httpMatchRequest.getUri().getMatchType() instanceof ExactMatchType) {
                ExactMatchType exactMatchType = (ExactMatchType) httpMatchRequest.getUri().getMatchType();
                myStringMatch.setRouteCondition("uri");
                myStringMatch.setMatchType("exact");
                myStringMatch.setInputRule(exactMatchType.getExact());
            }

            if (httpMatchRequest.getUri().getMatchType() instanceof RegexMatchType) {
                RegexMatchType regexMatchType = (RegexMatchType) httpMatchRequest.getUri().getMatchType();
                myStringMatch.setRouteCondition("uri");
                myStringMatch.setMatchType("regex");
                myStringMatch.setInputRule(regexMatchType.getRegex());
            }

            return myStringMatch;
        }).toArray(VirtualserviceDetailDTO.HTTPMatchRequest[]::new);
    }

}
